001    /*
002     * Copyright 2005 Stephen J. McConnell.
003     *
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.transit.console;
020    
021    import java.io.IOException;
022    import java.io.OutputStream;
023    import java.io.File;
024    import java.io.FileNotFoundException;
025    import java.lang.reflect.InvocationTargetException;
026    import java.net.URI;
027    import java.net.URL;
028    import java.util.List;
029    import java.util.Set;
030    import java.util.HashSet;
031    
032    import net.dpml.cli.Option;
033    import net.dpml.cli.Group;
034    import net.dpml.cli.CommandLine;
035    import net.dpml.cli.commandline.Parser;
036    import net.dpml.cli.util.HelpFormatter;
037    import net.dpml.cli.OptionException;
038    import net.dpml.cli.DisplaySetting;
039    import net.dpml.cli.builder.ArgumentBuilder;
040    import net.dpml.cli.builder.GroupBuilder;
041    import net.dpml.cli.builder.DefaultOptionBuilder;
042    import net.dpml.cli.builder.CommandBuilder;
043    import net.dpml.cli.validation.URIValidator;
044    import net.dpml.cli.validation.URLValidator;
045    import net.dpml.cli.validation.NumberValidator;
046    
047    import net.dpml.util.Logger;
048    import net.dpml.lang.UnknownKeyException;
049    import net.dpml.lang.ValueDirective;
050    
051    import net.dpml.lang.Part;
052    import net.dpml.lang.PartException;
053    
054    import net.dpml.transit.Artifact;
055    import net.dpml.transit.Transit;
056    import net.dpml.transit.TransitBuilder;
057    import net.dpml.transit.DefaultTransitModel;
058    import net.dpml.transit.info.TransitDirective;
059    import net.dpml.transit.info.ProxyDirective;
060    import net.dpml.transit.info.CacheDirective;
061    import net.dpml.transit.info.HostDirective;
062    import net.dpml.transit.info.LayoutDirective;
063    import net.dpml.util.ExceptionHelper;
064    
065    
066    /**
067     * Transit Plugin that provides support for the configuration of the Transit subsystem.
068     *
069     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
070     * @version 1.0.1
071     */
072    public class TransitConsoleHandler 
073    {
074        // ------------------------------------------------------------------------
075        // state
076        // ------------------------------------------------------------------------
077    
078        private final Logger m_logger;
079        private final TransitDirective m_directive;
080        private final CommandLine m_line;
081        private final TransitDirectiveBuilder m_builder;
082        
083        // ------------------------------------------------------------------------
084        // constructor
085        // ------------------------------------------------------------------------
086    
087       /**
088        * Creation of a new Transit CLI handler.
089        * 
090        * @param logger the assigned logging channel
091        * @param args command line arguments
092        * @exception Exception if an error occurs during plugin establishment
093        */
094        public TransitConsoleHandler( Logger logger, String[] args ) throws Exception
095        {
096            this( logger, getCommandLine( logger, args ) );
097        }
098        
099       /**
100        * Creation of a new Transit CLI handler.
101        *
102        * @param logger the assigned logging channel
103        * @param line the command line
104        * @exception Exception if an error occurs during plugin establishment
105        */
106        private TransitConsoleHandler( final Logger logger, final CommandLine line ) throws Exception
107        {
108            if( null == line ) 
109            {
110                m_line = null;
111                m_directive = null;
112                m_builder = null;
113                m_logger = null;
114                System.exit( -1 );
115            }
116            else
117            {
118                m_logger = logger;
119                m_line = line;
120                m_directive = loadTransitDirective( line );
121                m_builder = new TransitDirectiveBuilder( m_directive );
122                if( null == m_directive )
123                {
124                    System.exit( -1 );
125                }
126            }
127            
128            // handle command
129            
130            if( line.hasOption( INFO_COMMAND ) )
131            {
132                processInfo( line );
133            }
134            else if( line.hasOption( ADD_COMMAND ) )
135            {
136                TransitDirective directive = processAdd( line );
137                export( directive );
138            }
139            else if( line.hasOption( SET_COMMAND ) )
140            {
141                TransitDirective directive = processSet( line );
142                export( directive );
143            }
144            else if( line.hasOption( REMOVE_COMMAND ) )
145            {
146                TransitDirective directive = processRemove( line );
147                export( directive );
148            }
149            else if( line.hasOption( REVERT_COMMAND ) )
150            {
151                export( TransitDirective.CLASSIC_PROFILE );
152            }
153            else if( line.hasOption( LOAD_COMMAND ) )
154            {
155                processLoad( line );
156            }
157            else
158            {
159                processHelp();
160            }
161        }
162        
163        // ------------------------------------------------------------------------
164        // command handling
165        // ------------------------------------------------------------------------
166        
167        private void processLoad( CommandLine line )
168        {
169            try
170            {
171                URI uri = (URI) line.getValue( LOAD_COMMAND, null );
172                List list = line.getValues( ARGUMENTS );
173                String[] args = (String[]) list.toArray( new String[ list.size() ] );
174                Part part = Part.load( uri );
175                Object instance = part.instantiate( new Object[]{args} );
176                if( instance instanceof Runnable )
177                {
178                    Runnable runnable = (Runnable) instance;
179                    runnable.run();
180                }
181            }
182            catch( PartException e )
183            {
184                boolean stack = isDebugEnabled();
185                String message = ExceptionHelper.packException( e, stack ); 
186                System.out.println( message );
187            }
188            catch( Throwable e )
189            {
190                processException( e );
191            }
192        }
193        
194        private void processException( Throwable e )
195        {
196            if( e instanceof InvocationTargetException )
197            {
198                InvocationTargetException ite = (InvocationTargetException) e;
199                Throwable cause = ite.getCause();
200                processException( cause );
201            }
202            else
203            {
204                final String error = 
205                  "Part exception.";
206                String message = ExceptionHelper.packException( error, e, true ); 
207                System.out.println( message );
208            }
209        }
210        
211        private boolean isDebugEnabled()
212        {
213            return "true".equals( System.getProperty( "dpml.debug", "false" ) );
214        }
215        
216        private void processInfo( CommandLine line )
217        {
218            StringBuffer buffer = new StringBuffer();
219            
220            buffer.append( "\n\n  Version " + Transit.VERSION );
221            buffer.append( "\n\n  Environment\n" );
222            buffer.append( "\n    ${dpml.home} \t" + Transit.DPML_HOME );
223            buffer.append( "\n    ${dpml.data} \t" + Transit.DPML_DATA );
224            buffer.append( "\n    ${dpml.prefs} \t" + Transit.DPML_PREFS );
225            buffer.append( "\n    ${dpml.system} \t" + Transit.DPML_SYSTEM );
226            
227            ProxyDirective proxy = m_directive.getProxyDirective();
228            if( null != proxy )
229            {
230                buffer.append( "\n\n  Proxy Settings" );
231                buffer.append( "\n\n    Host: \t" + proxy.getHost() );
232                if( proxy.getUsername() != null )
233                {
234                    buffer.append( "\n    Username: \t" + proxy.getUsername() );
235                }
236                char[] pswd = proxy.getPassword();
237                if( null != pswd )
238                {
239                    buffer.append( "\n    Password: \t" );
240                    for( int i=0; i<pswd.length; i++ )
241                    {
242                        buffer.append( "*" );
243                    }
244                }
245                String[] excludes = proxy.getExcludes();
246                if( excludes.length > 0 )
247                {
248                    buffer.append( "\n    Excludes: \t" );
249                    for( int i=0; i<excludes.length; i++ )
250                    {
251                        String exclude = excludes[i];
252                        buffer.append( exclude );
253                        if( i < ( excludes.length-1 ) )
254                        {
255                            buffer.append( ", " );
256                        }
257                    }
258                }
259            }
260            
261            CacheDirective cache = m_directive.getCacheDirective();
262            
263            buffer.append( "\n\n  Cache and System Settings\n" );
264            buffer.append( "\n    Cache Layout: \t" + cache.getCacheLayout() );
265            buffer.append( "\n    Cache Directory: \t" + cache.getCache() );
266            buffer.append( "\n    System Directory: \t" + cache.getLocal() );
267            buffer.append( "\n    System Layout: \t" + cache.getLocalLayout() );
268            
269            HostDirective[] hosts = cache.getHostDirectives();
270            if( hosts.length > 0 )
271            {
272                buffer.append( "\n\n  Host Settings" );
273                for( int i=0; i<hosts.length; i++ )
274                {
275                    HostDirective host = hosts[i];
276                    buffer.append( "\n\n    " + host.getID() + " (" + ( i+1 ) + ")" );
277                    buffer.append( "\n\n      URL\t" + host.getHost() );
278                    buffer.append( "\n      Priority:\t" + host.getPriority() );
279                    if( host.getIndex() != null )
280                    {
281                        buffer.append( "\n      Index: \t" + host.getIndex() );
282                    }
283                    buffer.append( "\n      Enabled: \t" + host.getEnabled() );
284                    buffer.append( "\n      Trusted: \t" + host.getTrusted() );
285                    buffer.append( "\n      Layout: \t" + host.getLayout() );
286                    if( host.getUsername() != null )
287                    {
288                        buffer.append( "\n      Username: \t" + host.getUsername() );
289                    }
290                    buffer.append( "\n      Password: \t" );
291                    char[] pswd = host.getPassword();
292                    if( null != pswd )
293                    {
294                        for( int j=0; j<pswd.length; j++ )
295                        {
296                            buffer.append( "*" );
297                        }
298                    }
299                    buffer.append( "\n      Prompt: \t" + host.getPrompt() );
300                    buffer.append( "\n      Scheme: \t" + host.getScheme() );
301                }
302            }
303            else
304            {
305                buffer.append( "\n\n  No hosts." );
306            }
307            
308            LayoutDirective[] layouts = cache.getLayoutDirectives();
309            if( layouts.length > 0 )
310            {
311                buffer.append( "\n\n  Layout Settings" );
312                for( int i=0; i<layouts.length; i++ )
313                {
314                    LayoutDirective layout = layouts[i];
315                    buffer.append( "\n\n    " + layout.getID() + " (" + ( i+1 ) + ")" );
316                    buffer.append( "\n\n      Codebase:\t" + layout.getCodeBaseURI() );
317                    buffer.append( "\n      Title:\t" + layout.getTitle() );
318                }
319            }
320            
321            System.out.println( buffer.toString() );
322        }
323        
324        private TransitDirective processAdd( CommandLine line ) throws Exception
325        {
326            if( line.hasOption( ADD_HOST_COMMAND ) )
327            {
328                String key = (String) line.getValue( ADD_HOST_COMMAND, null );
329                CacheDirective cache = m_directive.getCacheDirective();
330                HostDirective[] hosts = cache.getHostDirectives();
331                for( int i=0; i<hosts.length; i++ )
332                {
333                    HostDirective h = hosts[i];
334                    if( h.getID().equals( key ) )
335                    {
336                        System.out.println( "ERROR: Host id '" + key + "' already assigned." );
337                        return null;
338                    }
339                }
340                
341                System.out.println( "Adding host: " + key );
342                URL url = (URL) line.getValue( REQUIRED_HOST_OPTION, null );
343                String username = (String) line.getValue( USERNAME_OPTION, null );
344                String password = (String) line.getValue( PASSWORD_OPTION, null );
345                Number priority = (Number) line.getValue( HOST_PRIORITY_OPTION, new Integer( 100 ) );
346                String index = (String) line.getValue( HOST_INDEX_OPTION, null );
347                boolean enabled = !line.hasOption( DISABLED_OPTION );
348                boolean trusted = line.hasOption( TRUSTED_OPTION );
349                String layout = (String) line.getValue( LAYOUT_OPTION, "classic" );
350                String scheme = (String) line.getValue( HOST_SCHEME_OPTION, "" );
351                String prompt = (String) line.getValue( HOST_PROMPT_OPTION, "" );
352                
353                HostDirective host = 
354                  new HostDirective( 
355                    key, priority.intValue(), url.toString(), index, username, 
356                    toCharArray( password ), enabled, trusted, layout, 
357                    scheme, prompt );
358                    
359                HostDirective[] newHosts = new HostDirective[ hosts.length + 1 ];
360                for( int i=0; i<hosts.length; i++ )
361                {
362                    newHosts[i] = hosts[i];
363                }
364                newHosts[ hosts.length ] = host;
365                CacheDirectiveBuilder builder = new CacheDirectiveBuilder( cache );
366                CacheDirective newCache = builder.create( newHosts );
367                return m_builder.create( newCache );
368            }
369            else if( line.hasOption( ADD_LAYOUT_COMMAND ) )
370            {
371                String key = (String) line.getValue( ADD_LAYOUT_COMMAND, null );
372                CacheDirective cache = m_directive.getCacheDirective();
373                LayoutDirective[] handlers = cache.getLayoutDirectives();
374                for( int i=0; i<handlers.length; i++ )
375                {
376                    LayoutDirective c = handlers[i];
377                    if( c.getID().equals( key ) )
378                    {
379                        System.out.println( "ERROR: Layout id '" + key + "' already assigned." );
380                        return null;
381                    }
382                }
383                
384                System.out.println( "Adding layout: " + key );
385                URI uri = (URI) line.getValue( REQUIRED_CODEBASE_OPTION, null );
386                String title = (String) line.getValue( TITLE_OPTION, null );
387                
388                LayoutDirective handler = 
389                  new LayoutDirective( 
390                    key, title, uri, new ValueDirective[0] );
391                
392                LayoutDirective[] newHandlers = new LayoutDirective[ handlers.length + 1 ];
393                for( int i=0; i<handlers.length; i++ )
394                {
395                    newHandlers[i] = handlers[i];
396                }
397                newHandlers[ handlers.length ] = handler;
398                CacheDirectiveBuilder builder = new CacheDirectiveBuilder( cache );
399                CacheDirective newCache = builder.create( newHandlers );
400                return m_builder.create( newCache );
401            }
402            else
403            {
404                throw new IllegalStateException( "Unqualified add." );
405            }
406        }
407        
408        private TransitDirective processSet( CommandLine line ) throws Exception
409        {
410            if( line.hasOption( SET_CACHE_COMMAND ) )
411            {
412                return setCache( line );
413            }
414            else if( line.hasOption( SET_SYSTEM_COMMAND ) )
415            {
416                return setSystem( line );
417            }
418            else if( line.hasOption( PROXY_COMMAND ) )
419            {
420                return setProxy( line );
421            }
422            else if( line.hasOption( SET_HOST_COMMAND ) )
423            {
424                return setHost( line );
425            }
426            else if( line.hasOption( SET_LAYOUT_COMMAND ) )
427            {
428                return setLayout( line );
429            }
430            else
431            {
432                throw new IllegalStateException( "Unqualified set command." );
433            }
434        }
435        
436        private TransitDirective setCache( CommandLine line ) throws Exception
437        {
438            String cache = (String) line.getValue( DIRECTORY_OPTION );
439            String layout = (String) line.getValue( LAYOUT_OPTION );
440            CacheDirective directive = m_directive.getCacheDirective();
441            CacheDirectiveBuilder builder = new CacheDirectiveBuilder( directive );
442            CacheDirective newCache = builder.create( cache, layout, null, null );
443            return m_builder.create( newCache );
444        }
445        
446        private TransitDirective setSystem( CommandLine line ) throws Exception
447        {
448            String system = (String) line.getValue( DIRECTORY_OPTION );
449            String layout = (String) line.getValue( LAYOUT_OPTION );
450            CacheDirective directive = m_directive.getCacheDirective();
451            CacheDirectiveBuilder builder = new CacheDirectiveBuilder( directive );
452            CacheDirective newCache = builder.create( null, null, system, layout );
453            return m_builder.create( newCache );
454        }
455        
456        private TransitDirective setProxy( CommandLine line ) throws Exception
457        {
458            System.out.println( "Updating proxy configuration." );
459            ProxyDirective proxy = m_directive.getProxyDirective();
460            
461            URL url = (URL) line.getValue( HOST_OPTION, null );
462            String username = (String) line.getValue( USERNAME_OPTION, null );
463            String password = (String) line.getValue( PASSWORD_OPTION, null );
464            List values = line.getValues( PROXY_EXCLUDE_OPTION );
465            String[] excludes = (String[]) values.toArray( new String[0] );
466            char[] pswd = toCharArray( password );
467            if( null == proxy )
468            {
469                if( null == url )
470                {
471                    System.out.println( "ERROR: Missing proxy host option." );
472                    return null;
473                }
474                ProxyDirective p = new ProxyDirective( 
475                  url.toString(), excludes, username, pswd );
476                return m_builder.create( p );
477            }
478            else
479            {
480                ProxyDirectiveBuilder builder = new ProxyDirectiveBuilder( proxy );
481                ProxyDirective p = builder.create( url, excludes, username, pswd );
482                return m_builder.create( p );
483            }
484        }
485        
486        private TransitDirective setHost( CommandLine line ) throws Exception
487        {
488            String key = (String) line.getValue( SET_HOST_COMMAND, null );
489            CacheDirective cache = m_directive.getCacheDirective();
490            HostDirective[] hosts = cache.getHostDirectives();
491            HostDirective host = null;
492            for( int i=0; i<hosts.length; i++ )
493            {
494                HostDirective h = hosts[i];
495                if( h.getID().equals( key ) )
496                {
497                    host = h;
498                }
499            }
500            
501            if( null == host )
502            {
503                System.out.println( "ERROR: Host id '" + key + "' not recognized." );
504                return null;
505            }
506            
507            System.out.println( "Updating host: " + key );
508            URL url = (URL) line.getValue( HOST_OPTION, null );
509            int priority = getPriorityValue( line );
510            String index = (String) line.getValue( HOST_INDEX_OPTION, null );
511            String username = (String) line.getValue( USERNAME_OPTION, null );
512            String password = (String) line.getValue( PASSWORD_OPTION, null );
513            boolean enabled = getEnabledFlag( line, host );
514            boolean trusted = getTrustedFlag( line, host );
515            String layout = (String) line.getValue( LAYOUT_OPTION, null );
516            String scheme = (String) line.getValue( HOST_SCHEME_OPTION, null );
517            String prompt = (String) line.getValue( HOST_PROMPT_OPTION, null );
518            
519            HostDirectiveBuilder builder = new HostDirectiveBuilder( host );
520            HostDirective newHost = 
521              builder.create( 
522                priority, url, index, username, toCharArray( password ),
523                enabled, trusted, layout, scheme, prompt );
524            
525            HostDirective[] newHosts = new HostDirective[ hosts.length ];
526            for( int i=0; i<hosts.length; i++ )
527            {   
528                HostDirective h = hosts[i];
529                if( h.getID().equals( key ) )
530                {
531                    newHosts[i] = newHost;
532                }
533                else
534                {
535                    newHosts[i] = h;
536                }
537            }
538            CacheDirectiveBuilder cacheBuilder = new CacheDirectiveBuilder( cache );
539            CacheDirective newCache = cacheBuilder.create( newHosts );
540            return m_builder.create( newCache );
541        }
542        
543        private TransitDirective setLayout( CommandLine line ) throws Exception
544        {
545            String key = (String) line.getValue( SET_LAYOUT_COMMAND, null );
546            CacheDirective cache = m_directive.getCacheDirective();
547            LayoutDirective[] handlers = cache.getLayoutDirectives();
548            LayoutDirective handler = null;
549            for( int i=0; i<handlers.length; i++ )
550            {
551                LayoutDirective c = handlers[i];
552                if( c.getID().equals( key ) )
553                {
554                    handler = c;
555                }
556            }
557    
558            if( null == handler )
559            {
560                System.out.println( "ERROR: Layout id '" + key + "' not recognized." );
561                return null;
562            }
563                
564            System.out.println( "Updating layout: " + key );
565            URI uri = (URI) line.getValue( CODEBASE_OPTION, null );
566            String title = (String) line.getValue( TITLE_OPTION, null );
567                
568            LayoutDirectiveBuilder builder = new LayoutDirectiveBuilder( handler );
569            LayoutDirective newDirective = 
570              builder.create( title, uri, null );
571                
572            LayoutDirective[] newDirectives = new LayoutDirective[ handlers.length ];
573            for( int i=0; i<handlers.length; i++ )
574            {   
575                LayoutDirective d = handlers[i];
576                if( d.getID().equals( key ) )
577                {
578                    newDirectives[i] = newDirective;
579                }
580                else
581                {
582                    newDirectives[i] = d;
583                }
584            }
585            CacheDirectiveBuilder cacheBuilder = new CacheDirectiveBuilder( cache );
586            CacheDirective newCache = cacheBuilder.create( newDirectives );
587            return m_builder.create( newCache );
588        }
589        
590        private TransitDirective processRemove( CommandLine line )
591        {
592            if( line.hasOption( SELECT_HOST_COMMAND ) )
593            {
594                String key = (String) line.getValue( SELECT_HOST_COMMAND, null );
595                System.out.println( "Removing resource host: " + key );
596                CacheDirective cache = m_directive.getCacheDirective();
597                CacheDirectiveBuilder builder = new CacheDirectiveBuilder( cache );
598                try
599                {
600                    CacheDirective newCache = builder.removeHostDirective( key );
601                    return m_builder.create( newCache );
602                }
603                catch( UnknownKeyException e )
604                {
605                    System.out.println( "Unknown host '" + key + "'." );
606                    return null;
607                }
608            }
609            else if( line.hasOption( SELECT_LAYOUT_COMMAND ) )
610            {
611                String key = (String) line.getValue( SELECT_LAYOUT_COMMAND, null );
612                System.out.println( "Removing layout: " + key );
613                CacheDirective cache = m_directive.getCacheDirective();
614                CacheDirectiveBuilder builder = new CacheDirectiveBuilder( cache );
615                try
616                {
617                    CacheDirective newCache = builder.removeLayoutDirective( key );
618                    return m_builder.create( newCache );
619                }
620                catch( UnknownKeyException e )
621                {
622                    System.out.println( "Unknown layout '" + key + "'." );
623                    return null;
624                }
625            }
626            else if( line.hasOption( REMOVE_PROXY_COMMAND ) )
627            {
628                if( null == m_directive.getProxyDirective() )
629                {
630                    System.out.println( "Nothing to remove." );
631                    return null;
632                }
633                else
634                {
635                    System.out.println( "Removing all proxy settings." );
636                    CacheDirective cache = m_directive.getCacheDirective();
637                    return new TransitDirective( null, cache );
638                }
639            }
640            else
641            {
642                throw new IllegalStateException( "Unqualified remove command." );
643            }
644        }
645        
646        private void export( TransitDirective directive )
647        {
648            if( null == directive )
649            {
650                return;
651            }
652            else if( m_directive.equals( directive ) )
653            {
654                System.out.println( "# no change" );
655            }
656            else
657            {
658                URI store = DefaultTransitModel.DEFAULT_PROFILE_URI;
659                URI uri = (URI) m_line.getValue( PROFILE_URI_OPTION, store );
660                System.out.println( "Saving to: " + uri );
661                OutputStream output = null;
662                try
663                {
664                    URL url = buildURL( uri );
665                    output = url.openConnection().getOutputStream();
666                    TransitBuilder builder = new TransitBuilder( m_logger );
667                    builder.write( directive, output );
668                }
669                catch( Exception e )
670                {
671                    final String error = 
672                      "Internal error while attempting to write to the uri: "
673                      + uri;
674                    getLogger().error( error, e );
675                }
676                finally
677                {
678                    if( null != output )
679                    {
680                        try
681                        {
682                            output.close();
683                        }
684                        catch( Exception e )
685                        {
686                            e.printStackTrace();
687                        }
688                    }
689                }
690            }
691        }
692        
693        // ------------------------------------------------------------------------
694        // internal utilities
695        // ------------------------------------------------------------------------
696        
697        private Logger getLogger()
698        {
699            return m_logger;
700        }
701        
702        private char[] toCharArray( String value )
703        {
704            if( null == value )
705            {
706                return null;
707            }
708            else
709            {
710                return value.toCharArray();
711            }
712        }
713        
714        private TransitDirective loadTransitDirective( CommandLine line )
715        {
716            if( line.hasOption( PROFILE_URI_OPTION ) )
717            {
718                URI uri = (URI) line.getValue( PROFILE_URI_OPTION, null );
719                try
720                {
721                    URL url = buildURL( uri );
722                    TransitBuilder builder = new TransitBuilder( m_logger );
723                    return builder.load( url );
724                }
725                catch( FileNotFoundException e )
726                {
727                    final String error = 
728                      "Resource not found: "
729                    + uri;
730                    getLogger().warn( error );
731                    return null;
732                }
733                catch( Exception e )
734                {
735                    final String error = 
736                      "An error occured while attempting to load the transit profile: "
737                      + uri;
738                    getLogger().error( error, e  );
739                    return null;
740                }
741            }
742            else
743            {
744                File prefs = Transit.DPML_PREFS;
745                File config = new File( prefs, "dpml/transit/xmls/standard.xml" );
746                if( config.exists() )
747                {
748                    try
749                    {
750                        URL url = config.toURL();
751                        TransitBuilder builder = new TransitBuilder( m_logger );
752                        return builder.load( url );
753                    }
754                    catch( Exception e )
755                    {
756                        final String error = 
757                          "An error occured while attempting to load the transit profile: "
758                        + config;
759                        getLogger().error( error, e  );
760                        return null;
761                    }
762                }
763                else
764                {
765                    //
766                    // load default profile
767                    //
768                    
769                    return TransitDirective.CLASSIC_PROFILE;
770                }
771            }
772        }
773        
774        private URL buildURL( URI uri ) throws IOException
775        {
776            if( Artifact.isRecognized( uri ) )
777            {
778                return Artifact.createArtifact( uri ).toURL();
779            }
780            else
781            {
782                return uri.toURL();
783            }
784          
785        }
786        
787        private boolean getEnabledFlag( CommandLine line, HostDirective host )
788        {
789            if( line.hasOption( DISABLED_OPTION ) )
790            {
791                return false;
792            }
793            else if( line.hasOption( ENABLED_OPTION ) )
794            {
795                return true;
796            }
797            else
798            {
799                return host.getEnabled();
800            }
801        }
802        
803        private boolean getTrustedFlag( CommandLine line, HostDirective host )
804        {
805            if( line.hasOption( TRUSTED_OPTION ) )
806            {
807                return true;
808            }
809            else if( line.hasOption( UNTRUSTED_OPTION ) )
810            {
811                return false;
812            }
813            else
814            {
815                return host.getTrusted();
816            }
817        }
818        
819        private int getPriorityValue( CommandLine line )
820        {
821            if( line.hasOption( HOST_PRIORITY_OPTION ) )
822            {
823                Number priority = (Number) line.getValue( HOST_PRIORITY_OPTION, new Integer( -1 ) );
824                return priority.intValue();
825            }
826            else
827            {
828                return -1;
829            }
830        }
831        
832        // ------------------------------------------------------------------------
833        // static utilities
834        // ------------------------------------------------------------------------
835        
836        private static final DefaultOptionBuilder OPTION_BUILDER = new DefaultOptionBuilder();
837        private static final ArgumentBuilder ARGUMENT_BUILDER = new ArgumentBuilder();
838        private static final CommandBuilder COMMAND_BUILDER = new CommandBuilder();
839        private static final GroupBuilder GROUP_BUILDER = new GroupBuilder();
840        private static final NumberValidator INTERGER_VALIDATOR = NumberValidator.getIntegerInstance();
841        private static final Set HELP_TOPICS = new HashSet();
842        
843        static
844        {
845            HELP_TOPICS.add( "add" );
846            HELP_TOPICS.add( "set" );
847            HELP_TOPICS.add( "remove" );
848        }
849        
850        private static final Option DIRECTORY_OPTION = 
851            OPTION_BUILDER
852              .withShortName( "dir" )
853              .withDescription( "Directory." )
854              .withRequired( false )
855              .withArgument(
856                ARGUMENT_BUILDER 
857                  .withDescription( "Directory." )
858                  .withName( "path" )
859                  .withMinimum( 1 )
860                  .withMaximum( 1 )
861                  .create() )
862              .create();
863        
864        private static final Option SYSTEM_LIBRARY_OPTION = 
865            OPTION_BUILDER
866              .withShortName( "system" )
867              .withDescription( "Local system repository." )
868              .withRequired( false )
869              .withArgument(
870                ARGUMENT_BUILDER 
871                  .withDescription( "Directory." )
872                  .withName( "dir" )
873                  .withMinimum( 1 )
874                  .withMaximum( 1 )
875                  .create() )
876              .create();
877              
878        private static final Option PROFILE_URI_OPTION = 
879            OPTION_BUILDER
880              .withShortName( "profile" )
881              .withDescription( "Configuration profile uri." )
882              .withRequired( false )
883              .withArgument(
884                ARGUMENT_BUILDER 
885                  .withDescription( "URI path." )
886                  .withName( "uri" )
887                  .withMinimum( 1 )
888                  .withMaximum( 1 )
889                  .withValidator( new URIValidator() )
890                  .create() )
891              .create();
892        
893        private static final Option HELP_COMMAND =
894          COMMAND_BUILDER
895            .withName( "help" )
896            .withDescription( "Print command help." )
897            .create();
898            
899        private static final Option HOST_OPTION = 
900          OPTION_BUILDER
901            .withShortName( "url" )
902            .withDescription( "Host URL." )
903            .withRequired( false )
904            .withArgument(
905              ARGUMENT_BUILDER 
906                .withName( "url" )
907                .withMinimum( 1 )
908                .withMaximum( 1 )
909                .withValidator( new URLValidator() )
910                .create() )
911            .create();
912        
913        private static final Option REQUIRED_HOST_OPTION = 
914          OPTION_BUILDER
915            .withShortName( "url" )
916            .withDescription( "Host URL (required)." )
917            .withRequired( true )
918            .withArgument(
919              ARGUMENT_BUILDER 
920                .withName( "url" )
921                .withMinimum( 1 )
922                .withMaximum( 1 )
923                .withValidator( new URLValidator() )
924                .create() )
925            .create();
926        
927        private static final Option REQUIRED_CODEBASE_OPTION = 
928          OPTION_BUILDER
929            .withShortName( "uri" )
930            .withDescription( "Codebase URI (required)." )
931            .withRequired( true )
932            .withArgument(
933              ARGUMENT_BUILDER 
934                .withName( "uri" )
935                .withMinimum( 1 )
936                .withMaximum( 1 )
937                .withValidator( new URIValidator() )
938                .create() )
939            .create();
940        
941        private static final Option CODEBASE_OPTION = 
942          OPTION_BUILDER
943            .withShortName( "uri" )
944            .withDescription( "Codebase URI." )
945            .withRequired( false )
946            .withArgument(
947              ARGUMENT_BUILDER 
948                .withName( "uri" )
949                .withMinimum( 1 )
950                .withMaximum( 1 )
951                .withValidator( new URIValidator() )
952                .create() )
953            .create();
954        
955        private static final Option TITLE_OPTION = 
956          OPTION_BUILDER
957            .withShortName( "title" )
958            .withDescription( "Title." )
959            .withRequired( false )
960            .withArgument(
961              ARGUMENT_BUILDER 
962                .withName( "name" )
963                .withMinimum( 1 )
964                .withMaximum( 1 )
965                .create() )
966            .create();
967        
968        private static final Option USERNAME_OPTION = 
969          OPTION_BUILDER
970            .withShortName( "username" )
971            .withDescription( "Username." )
972            .withRequired( false )
973            .withArgument(
974              ARGUMENT_BUILDER 
975                .withName( "username" )
976                .withMinimum( 1 )
977                .withMaximum( 1 )
978                .create() )
979            .create();
980        
981        private static final Option PASSWORD_OPTION = 
982          OPTION_BUILDER
983            .withShortName( "password" )
984            .withDescription( "Password." )
985            .withRequired( false )
986            .withArgument(
987              ARGUMENT_BUILDER 
988                .withName( "password" )
989                .withMinimum( 1 )
990                .withMaximum( 1 )
991                .create() )
992            .create();
993    
994        private static final Option HOST_PRIORITY_OPTION = 
995          OPTION_BUILDER
996            .withShortName( "priority" )
997            .withDescription( "Host priority." )
998            .withRequired( false )
999            .withArgument(
1000              ARGUMENT_BUILDER 
1001                .withName( "int" )
1002                .withMinimum( 1 )
1003                .withMaximum( 1 )
1004                .withValidator( INTERGER_VALIDATOR )
1005                .create() )
1006            .create();
1007    
1008        private static final Option HOST_INDEX_OPTION = 
1009          OPTION_BUILDER
1010            .withShortName( "index" )
1011            .withDescription( "Host index path." )
1012            .withRequired( false )
1013            .withArgument(
1014              ARGUMENT_BUILDER 
1015                .withName( "resource" )
1016                .withMinimum( 1 )
1017                .withMaximum( 1 )
1018                .create() )
1019            .create();
1020            
1021        private static final Option LAYOUT_OPTION = 
1022          OPTION_BUILDER
1023            .withShortName( "layout" )
1024            .withDescription( "Host layout strategy." )
1025            .withRequired( false )
1026            .withArgument(
1027              ARGUMENT_BUILDER 
1028                .withName( "id" )
1029                .withMinimum( 1 )
1030                .withMaximum( 1 )
1031                .create() )
1032            .create();
1033            
1034        private static final Option HOST_SCHEME_OPTION = 
1035          OPTION_BUILDER
1036            .withShortName( "scheme" )
1037            .withDescription( "Host authentication scheme." )
1038            .withRequired( false )
1039            .withArgument(
1040              ARGUMENT_BUILDER 
1041                .withName( "scheme" )
1042                .withMinimum( 1 )
1043                .withMaximum( 1 )
1044                .create() )
1045            .create();
1046            
1047        private static final Option HOST_PROMPT_OPTION = 
1048          OPTION_BUILDER
1049            .withShortName( "prompt" )
1050            .withDescription( "Host authentication prompt." )
1051            .withRequired( false )
1052            .withArgument(
1053              ARGUMENT_BUILDER 
1054                .withName( "prompt" )
1055                .withMinimum( 1 )
1056                .withMaximum( 1 )
1057                .create() )
1058            .create();
1059        
1060        private static final Option TRUSTED_OPTION = 
1061          OPTION_BUILDER
1062            .withShortName( "trusted" )
1063            .withDescription( "Assert as trusted host." )
1064            .withRequired( false )
1065            .create();
1066            
1067        private static final Option UNTRUSTED_OPTION = 
1068          OPTION_BUILDER
1069            .withShortName( "untrusted" )
1070            .withDescription( "Assert as untrusted host." )
1071            .withRequired( false )
1072            .create();
1073        
1074        private static final Group TRUST_GROUP =
1075          GROUP_BUILDER
1076            .withMinimum( 0 )
1077            .withMaximum( 1 )
1078            .withOption( TRUSTED_OPTION )
1079            .withOption( UNTRUSTED_OPTION )
1080            .create();
1081        
1082        private static final Option ENABLED_OPTION = 
1083          OPTION_BUILDER
1084            .withShortName( "enabled" )
1085            .withDescription( "Enable the host." )
1086            .withRequired( false )
1087            .create();
1088            
1089        private static final Option DISABLED_OPTION = 
1090          OPTION_BUILDER
1091            .withShortName( "disabled" )
1092            .withDescription( "Disable the host." )
1093            .withRequired( false )
1094            .create();
1095            
1096        private static final Group ENABLED_GROUP =
1097          GROUP_BUILDER
1098            .withMinimum( 0 )
1099            .withMaximum( 1 )
1100            .withOption( ENABLED_OPTION )
1101            .withOption( DISABLED_OPTION )
1102            .create();
1103        
1104        private static final Option PROXY_EXCLUDE_OPTION = 
1105          OPTION_BUILDER
1106            .withShortName( "excludes" )
1107            .withDescription( "Proxy excludes." )
1108            .withRequired( false )
1109            .withArgument(
1110              ARGUMENT_BUILDER 
1111                .withName( "hosts" )
1112                .withMinimum( 1 )
1113                .withInitialSeparator( ' ' )
1114                .withSubsequentSeparator( ',' )
1115                .create() )
1116            .create();
1117            
1118        private static final Group SET_HANDLER_OPTIONS_GROUP =
1119          GROUP_BUILDER
1120            .withMinimum( 0 )
1121            .withOption( CODEBASE_OPTION )
1122            .withOption( TITLE_OPTION )
1123            .create();
1124        
1125        private static final Group ADD_HANDLER_OPTIONS_GROUP =
1126          GROUP_BUILDER
1127            .withOption( REQUIRED_CODEBASE_OPTION )
1128            .withOption( TITLE_OPTION )
1129            .create();
1130            
1131        private static final Group SET_HOST_OPTIONS_GROUP =
1132          GROUP_BUILDER
1133            .withMinimum( 0 )
1134            .withOption( HOST_OPTION )
1135            .withOption( USERNAME_OPTION )
1136            .withOption( PASSWORD_OPTION )
1137            .withOption( HOST_PRIORITY_OPTION )
1138            .withOption( HOST_INDEX_OPTION )
1139            .withOption( ENABLED_GROUP )
1140            .withOption( TRUST_GROUP )
1141            .withOption( LAYOUT_OPTION )
1142            .withOption( HOST_SCHEME_OPTION )
1143            .withOption( HOST_PROMPT_OPTION )
1144            .create();
1145        
1146        private static final Group ADD_HOST_OPTIONS_GROUP =
1147          GROUP_BUILDER
1148            .withMinimum( 0 )
1149            .withOption( REQUIRED_HOST_OPTION )
1150            .withOption( USERNAME_OPTION )
1151            .withOption( PASSWORD_OPTION )
1152            .withOption( HOST_PRIORITY_OPTION )
1153            .withOption( HOST_INDEX_OPTION )
1154            .withOption( DISABLED_OPTION )
1155            .withOption( TRUSTED_OPTION )
1156            .withOption( LAYOUT_OPTION )
1157            .withOption( HOST_SCHEME_OPTION )
1158            .withOption( HOST_PROMPT_OPTION )
1159            .create();
1160        
1161        private static final Group PROXY_OPTIONS_GROUP =
1162          GROUP_BUILDER
1163            .withMinimum( 0 )
1164            .withOption( HOST_OPTION )
1165            .withOption( USERNAME_OPTION )
1166            .withOption( PASSWORD_OPTION )
1167            .withOption( PROXY_EXCLUDE_OPTION )
1168            .create();
1169        
1170        private static final Group CACHE_OPTIONS_GROUP =
1171          GROUP_BUILDER
1172            .withMinimum( 0 )
1173            .withOption( DIRECTORY_OPTION )
1174            .withOption( LAYOUT_OPTION )
1175            .create();
1176        
1177        private static final Group SYSTEM_OPTIONS_GROUP =
1178          GROUP_BUILDER
1179            .withMinimum( 0 )
1180            .withOption( DIRECTORY_OPTION )
1181            .withOption( LAYOUT_OPTION )
1182            .create();
1183        
1184        private static final Option PROXY_COMMAND =
1185          COMMAND_BUILDER
1186            .withName( "proxy" )
1187            .withDescription( "Select proxy settings." )
1188            .withChildren( PROXY_OPTIONS_GROUP )
1189            .create();
1190        
1191        private static final Option SET_CACHE_COMMAND =
1192          COMMAND_BUILDER
1193            .withName( "cache" )
1194            .withDescription( "Select cache settings." )
1195            .withChildren( CACHE_OPTIONS_GROUP )
1196            .create();
1197        
1198        private static final Option SET_SYSTEM_COMMAND =
1199          COMMAND_BUILDER
1200            .withName( "system" )
1201            .withDescription( "Select system repository settings." )
1202            .withChildren( SYSTEM_OPTIONS_GROUP )
1203            .create();
1204        
1205        private static final Option ADD_HOST_COMMAND =
1206          COMMAND_BUILDER
1207            .withName( "host" )
1208            .withDescription( "Add a new resource host." )
1209            .withChildren( ADD_HOST_OPTIONS_GROUP )
1210            .withArgument(
1211              ARGUMENT_BUILDER 
1212                .withName( "id" )
1213                .withMinimum( 1 )
1214                .withMaximum( 1 )
1215                .create() )
1216            .create();
1217    
1218        private static final Option SET_HOST_COMMAND =
1219          COMMAND_BUILDER
1220            .withName( "host" )
1221            .withDescription( "Resource host selection." )
1222            .withChildren( SET_HOST_OPTIONS_GROUP )
1223            .withArgument(
1224              ARGUMENT_BUILDER 
1225                .withName( "id" )
1226                .withMinimum( 1 )
1227                .withMaximum( 1 )
1228                .create() )
1229            .create();
1230    
1231        private static final Option SET_LAYOUT_COMMAND =
1232          COMMAND_BUILDER
1233            .withName( "layout" )
1234            .withDescription( "Layout scheme selection." )
1235            .withChildren( SET_HANDLER_OPTIONS_GROUP )
1236            .withArgument(
1237              ARGUMENT_BUILDER 
1238                .withName( "id" )
1239                .withMinimum( 1 )
1240                .withMaximum( 1 )
1241                .create() )
1242            .create();
1243    
1244        private static final Option SELECT_HOST_COMMAND =
1245          COMMAND_BUILDER
1246            .withName( "host" )
1247            .withDescription( "Resource host selection." )
1248            .withArgument(
1249              ARGUMENT_BUILDER 
1250                .withName( "id" )
1251                .withMinimum( 1 )
1252                .withMaximum( 1 )
1253                .create() )
1254            .create();
1255    
1256        private static final Option SELECT_LAYOUT_COMMAND =
1257          COMMAND_BUILDER
1258            .withName( "layout" )
1259            .withDescription( "Layout scheme selection." )
1260            .withArgument(
1261              ARGUMENT_BUILDER 
1262                .withName( "id" )
1263                .withMinimum( 1 )
1264                .withMaximum( 1 )
1265                .create() )
1266            .create();
1267    
1268        private static final Option ADD_LAYOUT_COMMAND =
1269          COMMAND_BUILDER
1270            .withName( "layout" )
1271            .withDescription( "Add a new layout." )
1272            .withChildren( ADD_HANDLER_OPTIONS_GROUP )
1273            .withArgument(
1274              ARGUMENT_BUILDER 
1275                .withName( "id" )
1276                .withMinimum( 1 )
1277                .withMaximum( 1 )
1278                .create() )
1279            .create();
1280            
1281        private static final Option REMOVE_PROXY_COMMAND =
1282          COMMAND_BUILDER
1283            .withName( "proxy" )
1284            .withDescription( "Delete proxy settings." )
1285            .create();
1286            
1287        private static final Group ADD_OPTIONS_GROUP =
1288          GROUP_BUILDER
1289            .withMinimum( 1 )
1290            .withMinimum( 1 )
1291            .withOption( ADD_HOST_COMMAND )
1292            .withOption( ADD_LAYOUT_COMMAND )
1293            .create();
1294    
1295        private static final Group SET_OPTIONS_GROUP =
1296          GROUP_BUILDER
1297            .withMinimum( 1 )
1298            .withMinimum( 1 )
1299            .withOption( PROXY_COMMAND )
1300            .withOption( SET_CACHE_COMMAND )
1301            .withOption( SET_SYSTEM_COMMAND )
1302            .withOption( SET_HOST_COMMAND )
1303            .withOption( SET_LAYOUT_COMMAND )
1304            .create();
1305        
1306        private static final Group REMOVE_OPTIONS_GROUP =
1307          GROUP_BUILDER
1308            .withMinimum( 1 )
1309            .withMinimum( 1 )
1310            .withOption( SELECT_HOST_COMMAND )
1311            .withOption( SELECT_LAYOUT_COMMAND )
1312            .withOption( REMOVE_PROXY_COMMAND )
1313            .create();
1314        
1315        private static final Option ADD_COMMAND =
1316          COMMAND_BUILDER
1317            .withName( "add" )
1318            .withChildren( ADD_OPTIONS_GROUP )
1319            .create();
1320        
1321        private static final Option SET_COMMAND =
1322          COMMAND_BUILDER
1323            .withName( "set" )
1324            .withDescription( "Set an configuration aspect." )
1325            .withChildren( SET_OPTIONS_GROUP )
1326            .create();
1327        
1328        private static final Option REMOVE_COMMAND =
1329          COMMAND_BUILDER
1330            .withName( "remove" )
1331            .withChildren( REMOVE_OPTIONS_GROUP )
1332            .create();
1333        
1334        private static final Option INFO_COMMAND =
1335          COMMAND_BUILDER
1336            .withName( "info" )
1337            .withDescription( "List configuration." )
1338            .create();
1339    
1340        private static final Option REVERT_COMMAND =
1341          COMMAND_BUILDER
1342            .withName( "revert" )
1343            .withDescription( "Set configuration to default." )
1344            .create();
1345    
1346        private static final Option LOAD_COMMAND =
1347          COMMAND_BUILDER
1348            .withName( "load" )
1349            .withDescription( "Load a transit plugin." )
1350            .withArgument(
1351              ARGUMENT_BUILDER 
1352                .withName( "uri" )
1353                .withMinimum( 1 )
1354                .withMaximum( 1 )
1355                .withValidator( new URIValidator() )
1356                .create() )
1357            .create();
1358        
1359        private static final Option ARGUMENTS = 
1360            ARGUMENT_BUILDER
1361              .withName( "args" )
1362              .create();
1363    
1364        private static final Group COMMAND_GROUP =
1365          GROUP_BUILDER
1366            .withOption( INFO_COMMAND )
1367            .withOption( ADD_COMMAND )
1368            .withOption( SET_COMMAND )
1369            .withOption( REMOVE_COMMAND )
1370            .withOption( REVERT_COMMAND )
1371            .withOption( LOAD_COMMAND )
1372            .withOption( HELP_COMMAND )
1373            .withMinimum( 1 )
1374            .withMaximum( 1 )
1375            .create();
1376        
1377        private static final Group OPTIONS_GROUP =
1378          GROUP_BUILDER
1379            .withName( "command" )
1380            .withOption( PROFILE_URI_OPTION )
1381            .withOption( COMMAND_GROUP )
1382            .withOption( ARGUMENTS )
1383            .withMinimum( 0 )
1384            .create();
1385    
1386        private static CommandLine getCommandLine( Logger logger, String[] args )
1387        {
1388            try
1389            {
1390                // parse the command line arguments
1391            
1392                Parser parser = new Parser();
1393                parser.setGroup( OPTIONS_GROUP );
1394                return parser.parse( args );
1395            }
1396            catch( OptionException e )
1397            {
1398                logger.error( e.getMessage() );
1399                return null;
1400            }
1401        }
1402        
1403        private static void processHelp() throws IOException
1404        {
1405            processGeneralHelp( OPTIONS_GROUP );
1406        }
1407        
1408        private static void processGeneralHelp( Group group ) throws IOException
1409        {
1410            HelpFormatter formatter = new HelpFormatter( 
1411              HelpFormatter.DEFAULT_GUTTER_LEFT, 
1412              HelpFormatter.DEFAULT_GUTTER_CENTER, 
1413              HelpFormatter.DEFAULT_GUTTER_RIGHT, 
1414              100, 50 );
1415            
1416            formatter.getDisplaySettings().add( DisplaySetting.DISPLAY_GROUP_OUTER );
1417            formatter.getDisplaySettings().add( DisplaySetting.DISPLAY_PROPERTY_OPTION );
1418            formatter.getDisplaySettings().add( DisplaySetting.DISPLAY_ARGUMENT_BRACKETED );
1419            
1420            formatter.getFullUsageSettings().add( DisplaySetting.DISPLAY_OPTIONAL );
1421            formatter.getFullUsageSettings().add( DisplaySetting.DISPLAY_GROUP_OUTER );
1422            formatter.getFullUsageSettings().add( DisplaySetting.DISPLAY_PROPERTY_OPTION );
1423            formatter.getFullUsageSettings().add( DisplaySetting.DISPLAY_OPTIONAL );
1424            formatter.getFullUsageSettings().add( DisplaySetting.DISPLAY_ARGUMENT_BRACKETED );
1425            formatter.getFullUsageSettings().remove( DisplaySetting.DISPLAY_PARENT_CHILDREN );
1426            
1427            formatter.getLineUsageSettings().add( DisplaySetting.DISPLAY_PROPERTY_OPTION );
1428            formatter.getLineUsageSettings().add( DisplaySetting.DISPLAY_ARGUMENT_BRACKETED );
1429            formatter.getLineUsageSettings().remove( DisplaySetting.DISPLAY_PARENT_CHILDREN );
1430            formatter.getLineUsageSettings().remove( DisplaySetting.DISPLAY_GROUP_EXPANDED );
1431            
1432            formatter.setGroup( group );
1433            formatter.setShellCommand( "transit" );
1434            formatter.print();
1435        }
1436    }
1437